package com.lightsh.scrollview;

import com.lightsh.scroller.LOverScroller;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * 该控件支持多个view间的水平滚动。每个view内部可支持独立的垂直滚动。
 * @author Shu
 *
 */
public class HScrollViewGroup extends ViewGroup {
	
	private static final String TAG = "HScrollViewGroup";
	
    private static final int INVALID_SCREEN = -1;
    
    private boolean mFirstLayout = true;

    /**
     * 记录当前显示的view
     */
    private int mCurrentScreen;
    
    /**
     * 记录下一个需要显示的view。
     * 注意，这里不是指与当前view相邻的下一个，而是指紧接着将会滚动到的view；即如果用户没有其他操作，屏幕会自动滚动到mNextScreen。
     */
    private int mNextScreen = INVALID_SCREEN;
    private LOverScroller mScroller;
    
    /**
     * 用来计算滑动速度的工具类
     */
    private VelocityTracker mVelocityTracker;
    
    /**
     * 用户上次按下（滑动到）的x坐标
     */
    private float mLastMotionX;
    
    /**
     * 没有在滚动状态
     */
    private final static int TOUCH_STATE_REST = 0;
    
    /**
     * 正在滚动状态
     */
    private final static int TOUCH_STATE_SCROLLING = 1;

    private int mTouchState = TOUCH_STATE_REST;   

    /**
     * 当用户手势水平移动超过这个距离时，则认为是水平拖动操作。
     */
    private int mTouchSlop;
    /**
     * 记录水平滑动速度的最大值
     */
    private int mMaximumVelocity;
    
    /**
     * 当一个滑动手势达到这个速度时，将会使界面滑动到下一个view中去。
     */
    private int mMinimumVelocity;
    
    
    // overScroll 设置
    private int mOverScrollDistance;
    
    private int mCurrentOverScrollDistance;
    
    /**
     * 当滑动过程中放开手时，或者需要滚动到其中一个视图中时，每个视图的滚动时间的系数
     */
    private static final int COEFF_APPROACH_TO_EDGE = 400;
    
    /**
     * 当前活动的MotionEvent点的id值,处理多点触摸
     */
    private int mActivePointerId = INVALID_POINTER;
    
    /**
     * 当前没有活动点的标志
     */
    private static final int INVALID_POINTER = -1;
    
    /**
     * 是否支持over Scroll
     */
    static final int FLAG_OVERSCROLL                = 0x00000001;
    
    /**
     * 是否支持滚动结束后的shake
     */
    static final int FLAG_SHAKE                     = 0x00000002;
    
    
    private int mPrivateFlags = FLAG_OVERSCROLL | FLAG_SHAKE;
    
    
    private OnItemSelectedListener mItemSelectedListener;
    
    public HScrollViewGroup(Context context ){
        super(context);
        init();
    }
    /**
     * @param context
     * @param attrs
     */
    public HScrollViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public HScrollViewGroup(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
        init();
    }
    

    public OnItemSelectedListener getOnItemSelectedListener() {
        return mItemSelectedListener;
    }
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        this.mItemSelectedListener = listener;
    }

    /**
     * Initializes various states for this workspace.
     */
    private void init() {
        Context context = getContext();
        //初始化一个scroller来负责计算水平滚动的位移
        
        // 考虑提供一个interpolator(new OvershootInterpolator(1.0f)?)new BackInterpolator(Type.OUT, 2f));      
        mScroller = new LOverScroller(context); 
        changeScrollInterpolator();
        
        mCurrentScreen = 0;

        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        
        mTouchSlop = configuration.getScaledTouchSlop() * 2;
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mMinimumVelocity = 2 * configuration.getScaledMinimumFlingVelocity();
        
        final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
        final float density = metrics.density;
        
        
        // over scroll的最大距离
        mOverScrollDistance = (int) (density * 100 + 0.5f);
        logTip("mOverScrollDistance = " + mOverScrollDistance);
        logTip("mTouchSlop = " + mTouchSlop);
        logTip("mMaximumVelocity = " + mMaximumVelocity);
        logTip("mMinimumVelocity = " + mMinimumVelocity);
    }   
    
    @Override
    public void computeScroll() {
    	if (mScroller.computeScrollOffset()) {
        	int oldX = getScrollX();
            int x = mScroller.getCurrX();
            if(oldX != x){
            	overScrollBy(x - oldX, 0, getScrollX(), 0, getScrollRange(), 0, 
                		mOverScrollDistance, 0, false);
            }
            postInvalidate();
        } else {
        	// 当nextScreen为非INVALID_SCREEN时，并且滚动已经完成,需要调用回调
        	// 注意，这个回调是在shake之前调用的
    		if(mNextScreen != INVALID_SCREEN){
        		mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
                fireItemSelectedEvent(mCurrentScreen);
                mNextScreen = INVALID_SCREEN;
    		}
    		
        	// 是否需要shake回目标位置
        	if(mTouchState == TOUCH_STATE_REST){
            	final int currentX = getScrollX();
            	int desX = mCurrentScreen * getWidth();
            	if(desX != currentX){
            		// spring back
            		mScroller.springBackForShake(currentX, 0, desX, 0);
                    invalidate();
            	}
        	}
        }
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        boolean restore = false;
        int restoreCount = 0;

        // ViewGroup.dispatchDraw() supports many features we don't need:
        // clip to padding, layout animation, animation listener, disappearing
        // children, etc. The following implementation attempts to fast-track
        // the drawing dispatch by drawing only what we know needs to be drawn.
        if(getChildCount() == 0 ){
            return;
        }
        
        
        // 我们只需要绘制最多2个screen,并且直接根据getScrollX()判断即可
        final int currentX = getScrollX();
        final int width = getWidth();
        final int screenFirst = currentX / width;
        int nextScreen = -1;
        if(screenFirst * width > currentX){
        	nextScreen = screenFirst - 1;
        }else if(screenFirst * width < currentX){
        	nextScreen = screenFirst + 1;
        }
        
        final long drawingTime = getDrawingTime();
        if(nextScreen >= 0 && nextScreen < getChildCount()){
    		// 绘制出相邻的screen
    		drawChild(canvas, getChildAt(screenFirst), drawingTime);
    		drawChild(canvas, getChildAt(nextScreen), drawingTime);
        }else{
        	drawChild(canvas, getChildAt(screenFirst), drawingTime);
        }

        if (restore) {
            canvas.restoreToCount(restoreCount);
        }
    }    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("HScrollViewGroup can only be used in EXACTLY mode.");
        }

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("HScrollViewGroup can only be used in EXACTLY mode.");
        }

        // The children are given the same width and height as the workspace
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }


        if (mFirstLayout) {
            setHorizontalScrollBarEnabled(false);
            scrollTo(mCurrentScreen * width, 0);
            setHorizontalScrollBarEnabled(true);
            mFirstLayout = false;
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int childLeft = 0;

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                final int childWidth = child.getMeasuredWidth();                
                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onTouchEvent will be called and we do the actual
         * scrolling there.
         */

        /*
         * 当前状态是拖动状态并且用户手指正在屏幕移动，我们需要处理这个消息
         * 这是最常发生的情况，使用简洁的代码判断增加效率
         */
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
            return true;
        }
        
        switch (action) {
            case MotionEvent.ACTION_MOVE:{
            	final float x = ev.getX();
                /*
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                 * whether the user has moved far enough from his original down touch.
                 */
            	
            	final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    // 如果没有合法的活动点，我们不处理该消息
                    break;
                }

                /*
                 * Locally do absolute value. mLastMotionX is set to the y value
                 * of the down event.
                 */
            	if(isToucLeadScroll(x)){
            		mTouchState = TOUCH_STATE_SCROLLING;
            		// 需要在这里设置mLastMotionX，否则会造成界面刚开始滑动时的跳帧
            		mLastMotionX = x;
            		logTip("intercept MotionEvent.ACTION_MOVE return true!");
            	}
                break;
            }
            case MotionEvent.ACTION_DOWN:{
            	final float x = ev.getX();
                // Remember location of down touch
            	
            	// 记录活动点，这里消息的活动点一定是0，并且ev中应该只有一个点
            	mActivePointerId = ev.getPointerId(0);
            	
                mLastMotionX = x;
                /*
                 * If being flinged and user touches the screen, initiate drag;
                 * otherwise don't.  mScroller.isFinished should be false when
                 * being flinged.
                 */
                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
                if(mTouchState != TOUCH_STATE_REST){
                	logTip("intercept MotionEvent.ACTION_DOWN " + true);
                }
                break;
            }
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
            	mActivePointerId = INVALID_POINTER;
            	// 注意，当正在滚动时，这些事件一般是在onTouchEvent里面收到的...什么时候会在这里收到?
                // Release the drag
                mTouchState = TOUCH_STATE_REST;
                // 我们需要判断是否需要回弹(例如，当前可能view已经被拖动出边缘,然后用户松开手)
                if(mScroller.springBack(getScrollX(), getScrollY(), 0, getScrollRange(), 0, 0)){
                	invalidate();
                }
                logTip("intercept ACTION_CANCEL | ACTION_UP " + (mTouchState != TOUCH_STATE_REST));
                break;
            case MotionEvent.ACTION_POINTER_UP:
            	// 这个在多点触摸时，其中一点弹起
                onSecondaryPointerUp(ev);
                break;
                
        }

        /*
         * The only time we want to intercept motion events is if we are in the
         * drag mode.
         */
        return mTouchState != TOUCH_STATE_REST;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
    	
    	if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
            // Don't handle edge touches immediately -- they may actually belong to one of our
            // descendants.
            return false;
        }
    	
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        final int action = ev.getAction();
        float x = ev.getX();

        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:        	
        	// 按下动作时，进入scroll状态
        	mTouchState = TOUCH_STATE_SCROLLING;
        	
            /*
             * If being flinged and user touches, stop the fling. isFinished
             * will be false if being flinged.
             * 手指按下时，如果正在滑动，则停止
             */
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }

            // Remember where the motion event started
            mLastMotionX = x;
            
            changeCurrentScreen();
            
            // 记录活动点，这里消息的活动点一定是0，并且ev中应该只有一个点
            mActivePointerId = ev.getPointerId(0);
            break;
        case MotionEvent.ACTION_MOVE:
        	//logTip("onTouch ACTION_MOVE");
            if (mTouchState == TOUCH_STATE_SCROLLING) {
            	
            	int activePointerIndex = ev.findPointerIndex(mActivePointerId);
            	if(activePointerIndex < 0 || activePointerIndex > ev.getPointerCount() - 1){
            		// 我们需要换一个点
            		changeActivePoint(0, ev);
            		activePointerIndex = 0;
            	}
            	
            	x = ev.getX(activePointerIndex);
                // Scroll to follow the motion event
                final int deltaX = (int) (mLastMotionX - x);
                mLastMotionX = x;
                // 如果deltaX等于0，则认为没有移动
                if(deltaX != 0){
                    int moveDelta = LOverScroller.getOverMoveDelta(mCurrentOverScrollDistance, 
                    		isOverScrollEnable() ? mOverScrollDistance : 0, deltaX);
                    // 这个方法计算出scroll是否超出边界，并且通过onOverScrolled()回调结果。返回值表明是否超出了overscroll的最大值
                    
                    if(moveDelta != 0){
                    	if(overScrollBy(moveDelta, 0, getScrollX(), 0, getScrollRange(), 0, 
                        		isOverScrollEnable() ? mOverScrollDistance : 0, 0, true)){
                        	mVelocityTracker.clear();
                        }
                    }
                }
                
//                if (deltaX < 0) {
//                    if (mScrollX > 0) {
//                        scrollBy(Math.max(-mScrollX, deltaX), 0);
//                        mScrollX = getScrollX();
//                    }
//                } else if (deltaX > 0) {
//                    final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
//                            mScrollX - getWidth();
//                    if (availableToScroll > 0) {
//                        scrollBy(Math.min(availableToScroll, deltaX), 0);
//                        mScrollX = getScrollX();
//                    }
//                }
            }else{
            	// 这里设置这个状态,是因为有时候没有进入onInterceptTouchEvent就立马产生onTouchEvent()的ACTION_MOVE消息
            	if(isToucLeadScroll(x)){
            		mTouchState = TOUCH_STATE_SCROLLING;
            		mLastMotionX = x;
            		logErr("MotionEvent.ACTION_MOVE but mTouchState != TOUCH_STATE_SCROLLING && isToucLeadScroll()");
            	}
            }
            break;
        case MotionEvent.ACTION_UP:
            if (mTouchState == TOUCH_STATE_SCROLLING) {
                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
                
                // 是否需要回弹
                if(mScroller.springBack(getScrollX(), getScrollY(), 0, getScrollRange(), 0, 0)){
                	invalidate();
                }else{
                	// 判断速度是否能导致滑向下一个view
                    if ((velocityX > mMinimumVelocity) && mCurrentScreen > 0) {
                        // Fling hard enough to move left
                        snapToScreen(mCurrentScreen - 1);
                    } else if (velocityX < -mMinimumVelocity && mCurrentScreen < getChildCount() - 1) {
                        // Fling hard enough to move right
                        snapToScreen(mCurrentScreen + 1);
                    } else {
                        snapToDestination();
                    }
                }     

                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                
                // 只有在scroll状态下才需要清除?
                mTouchState = TOUCH_STATE_REST;
                mActivePointerId = INVALID_POINTER;
            }
            break;
        case MotionEvent.ACTION_CANCEL:
        	if(mTouchState == TOUCH_STATE_SCROLLING){
        		logTip("onTouch ACTION_CANCEL " + x);
        		if(mScroller.springBack(getScrollX(), getScrollY(), 0, getScrollRange(), 0, 0)){
                	invalidate();
                }
        		mTouchState = TOUCH_STATE_REST;
                mActivePointerId = INVALID_POINTER;
        	}        	
            break;
        case MotionEvent.ACTION_POINTER_UP:
        	// 这个在多点触摸时，其中一点弹起
        	onSecondaryPointerUp(ev);
        	break;
        }
        
        return true;
    }
    
    private void onSecondaryPointerUp(MotionEvent ev) {
        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
        	// 活动点up了，我们获取下一个点作为活动点
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            changeActivePoint(newPointerIndex, ev);
        }
    }
    private void changeActivePoint(int newPointerIndex, MotionEvent ev){
    	mLastMotionX = ev.getX(newPointerIndex);
        mActivePointerId = ev.getPointerId(newPointerIndex);
        if (mVelocityTracker != null) {
            mVelocityTracker.clear();
        }
    }
    

    private void snapToDestination() {
        final int screenWidth = getWidth();
        final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth;
        snapToScreen(whichScreen);
    }

    public void snapToScreen(int whichScreen) {
    	logTip("snapToScreen = " + whichScreen);
        //if (!mScroller.isFinished()) return;
        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        
        mNextScreen = whichScreen;
        
        final int newX = whichScreen * getWidth();
        final int delta = getSnapToScreenDelta(getScrollX(), newX);
        int duration = getSnapDuration(delta, getWidth());

        if (!mScroller.isFinished()) {
        	mScroller.abortAnimation();
        }
        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
        invalidate();
    }

    public void scrollLeft() {
        if (mScroller.isFinished()) {
            if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
        } else {
            if (mNextScreen > 0) snapToScreen(mNextScreen - 1);            
        }
    }

    public void scrollRight() {
        if (mScroller.isFinished()) {
            if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
        } else {
            if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);            
        }
    }
    
    private void fireItemSelectedEvent(int selectedIndex) {
		logTip("notify item change -> " + selectedIndex);
        if (mItemSelectedListener != null) {
            mItemSelectedListener.onSelected(selectedIndex);
        }
    }
    
    public void showScreen(int whichScreen) {       
        mCurrentScreen = whichScreen;
        mNextScreen = INVALID_SCREEN;
//        mScrollX = whichScreen * getWidth();
        scrollTo(whichScreen * getWidth(), 0);
        postInvalidate();
        fireItemSelectedEvent(whichScreen);
    }
    
    public interface OnItemSelectedListener {
        void onSelected(int selectedIndex);
    }

    public void showWaitProgress(int whichScreen){
    	if(whichScreen >= getChildCount()){
    		return;
    	}
    	FrameLayout fl = (FrameLayout)getChildAt(whichScreen);
    	LinearLayout ll = (LinearLayout)fl.getChildAt(0);
    	ll.setVisibility(View.VISIBLE);
    }
    
    public void hideWaitProgress(int whichScreen){
    	if(whichScreen >= getChildCount()){
    		return;
    	}
    	FrameLayout fl = (FrameLayout)getChildAt(whichScreen);
    	LinearLayout ll = (LinearLayout)fl.getChildAt(0);
    	ll.setVisibility(View.INVISIBLE);
    }
    
    public void showResultText(int whichScreen, String text){
    	if(whichScreen >= getChildCount()){
    		return;
    	}
    	FrameLayout fl = (FrameLayout)getChildAt(whichScreen);
    	TextView tvMsg = (TextView)fl.getChildAt(1);
    	tvMsg.setText(text);
    	tvMsg.setVisibility(View.VISIBLE);
    }
    
    public void hideResultText(int whichScreen){
    	if(whichScreen >= getChildCount()){
    		return;
    	}
    	FrameLayout fl = (FrameLayout)getChildAt(whichScreen);
    	TextView tvMsg = (TextView)fl.getChildAt(1);
    	tvMsg.setVisibility(View.INVISIBLE);
    }
	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
//		logTip("onScrollChanged: " + oldl + "->" + l);
		super.onScrollChanged(l, t, oldl, oldt);
	}
    
	private void logTip(String tip){
		Log.d(TAG, tip != null ? tip : "");
	}
	private void logErr(String err){
		Log.e(TAG, err != null ? err : "");
	}
	
	/**
	 *  该方法处理scroll改变，需调用View.scrollTo()方法进行实际的scroll
	 *  注意： 到这里并非一定会是发生了overScroll
	 */
	protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
			boolean clampedY) {
		super.scrollTo(scrollX, scrollY);
		
		// 计算overScroll距离
		final int currentScrollX = getScrollX();
		mCurrentOverScrollDistance = 0;
		if(currentScrollX < 0){
			mCurrentOverScrollDistance = getScrollX();
		}else if(currentScrollX > 0 && currentScrollX > getScrollRange()){
			mCurrentOverScrollDistance = currentScrollX - getScrollRange();
		}
		
	}
    
	/**
	 * 计算出scroll一共能够滚动的最大距离.overscroll时使用
	 */
	private int getScrollRange(){
		return (getChildCount() - 1) * getWidth();
	}
	
	private boolean isToucLeadScroll(float currentX){
		int dif = (int) Math.abs(mLastMotionX - currentX);
		return dif > mTouchSlop;
	}
	
	/**
	 * 根据需要滚动的距离，获取需要滚动的总时间
	 * @param distance
	 * @param screenWidth
	 * @return
	 */
	private int getSnapDuration(int distance, int screenWidth){
		float coeff = (float)Math.abs(distance) / screenWidth;
		if(coeff < 0.5){
			coeff = 0.5f;
		}else if(coeff > 2.5){
			coeff = 2.5f;
		}		
		return (int) (COEFF_APPROACH_TO_EDGE * coeff);
	}
	
	/**
	 * 这个方法计算出scroll是否超出边界，并且通过onOverScrolled()回调结果。返回值表明是否超出了overscroll的最大值
	 * 注意: 该方法在android 2.3才有，此处重写是为了兼容旧的系统.
	 */
	protected boolean overScrollBy(int deltaX, int deltaY,
            int scrollX, int scrollY,
            int scrollRangeX, int scrollRangeY,
            int maxOverScrollX, int maxOverScrollY,
            boolean isTouchEvent) {

		// 其实我们只关心x轴
        int newScrollX = scrollX + deltaX;
        int newScrollY = scrollY + deltaY;

        // 计算出允许的最大范围
        final int left = -maxOverScrollX;
        final int right = maxOverScrollX + scrollRangeX;
        final int top = -maxOverScrollY;
        final int bottom = maxOverScrollY + scrollRangeY;

        boolean clampedX = false;
        if (newScrollX > right) {
            newScrollX = right;
            clampedX = true;
        } else if (newScrollX < left) {
            newScrollX = left;
            clampedX = true;
        }

        boolean clampedY = false;
        if (newScrollY > bottom) {
            newScrollY = bottom;
            clampedY = true;
        } else if (newScrollY < top) {
            newScrollY = top;
            clampedY = true;
        }
        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
        return clampedX || clampedY;
    }
    
    /**
     * 获取snap到某一个screen的delta值。
     * 该值与目标位置有一点偏移(根据总的snap距离的比例算出)，用来实现停止前的晃动效果
     */
    private int getSnapToScreenDelta(int currentX, int desX){
    	final int width = getWidth();
    	final int realDelta = desX - currentX;    	
    	
    	int shakeDistance = 0;
    	if(isShakeEnable()){
    		final int maxDistance = width / 30;
        	final int minDistance = width / 80;
        	
        	// 当snap距离大于1/10屏幕才晃动
        	if(Math.abs(realDelta) > width / 10){
        		float coeff = (float)realDelta / width;
        		shakeDistance = (int) (coeff * maxDistance);
        		shakeDistance = Math.max(-maxDistance, Math.min(maxDistance, shakeDistance));
        		if(Math.abs(shakeDistance) < minDistance){
        			shakeDistance = (int) (Math.signum(realDelta) * minDistance);
        		}    		
        	}
    	}    	
    	return realDelta + shakeDistance;
    }
    
    /**
     * 当发生ACTION_DOWN事件时，需要重置mCurrentScreen的值
     */
    private void changeCurrentScreen(){
    	final int screenWidth = getWidth();
        final int whichScreen = Math.round((float)getScrollX()/screenWidth);
        if(mCurrentScreen != whichScreen){
        	mCurrentScreen = whichScreen;
        	fireItemSelectedEvent(mCurrentScreen);
        }
        // 始终需要将该值设置为无效
        mNextScreen = INVALID_SCREEN;
    }
    
    
    public void enableOverScroll(boolean enable){
    	if(enable){
    		mPrivateFlags |= FLAG_OVERSCROLL;
    	}else{
    		mPrivateFlags &= ~FLAG_OVERSCROLL;
    	}
    }
    
    public void enableShake(boolean enable){
    	if(enable){
    		if(!isEnabled()){
    			mPrivateFlags |= FLAG_SHAKE;
    			changeScrollInterpolator();
    		}
    	}else{
    		if(isEnabled()){
    			mPrivateFlags &= ~FLAG_SHAKE;
    			changeScrollInterpolator();
    		}
    	}
    }
    /**
     * 需要根据是否开启shake效果，来设置不同的interpolator
     */
    private void changeScrollInterpolator(){
    	// FIXME 选择一个好的interpolator
//    	if(isShakeEnable()){
//    		mScroller.setInterpolator(new OvershootInterpolator(0.8f));
//    	}else{
//    		mScroller.setInterpolator(null);
//    	}
    }
    
    public boolean isOverScrollEnable(){
    	return (mPrivateFlags & FLAG_OVERSCROLL) != 0;
    }
    
    public boolean isShakeEnable(){
    	return (mPrivateFlags & FLAG_SHAKE) != 0;
    }
}
